package compilationunit;


import java.io.*;

public class Parser {
	static final int _EOF = 0;
	static final int _ident = 1;
	static final int _enteros = 2;
	static final int _cadenaCar = 3;
	static final int _boolean = 4;
	static final int _char = 5;
	static final int _class = 6;
	static final int _false = 7;
	static final int _int = 8;
	static final int _new = 9;
	static final int _short = 10;
	static final int _static = 11;
	static final int _true = 12;
	static final int _void = 13;
	static final int _dosPuntos = 14;
	static final int _comma = 15;
	static final int _punto = 16;
	static final int _llaveIzda = 17;
	static final int _corcheteIzda = 18;
	static final int _parentesisIzda = 19;
	static final int _menos = 20;
	static final int _not = 21;
	static final int _mas = 22;
	static final int _llaveDer = 23;
	static final int _corcheteDer = 24;
	static final int _parentesisDer = 25;
	static final int _multiplicacion = 26;
	static final int _div = 27;
	static final int _menor = 28;
	static final int _mayor = 29;
	static final int _igual = 30;
	static final int _puntoComa = 31;
	static final int _doblesComillas = 32;
	static final int _interrogacion = 33;
	static final int maxT = 41;

	static final boolean T = true;
	static final boolean x = false;
	static final int minErrDist = 2;

	public static Token token;    // last recognized token   /* pdt */
	public static Token la;       // lookahead token
	static int errDist = minErrDist;

	

	static void SynErr (int n) {
		if (errDist >= minErrDist) Errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public static void SemErr (String msg) {
		if (errDist >= minErrDist) Errors.Error(token.line, token.col, msg); /* pdt */
		errDist = 0;
	}

	public static void SemError (String msg) {
		if (errDist >= minErrDist) Errors.Error(token.line, token.col, msg); /* pdt */
		errDist = 0;
	}

	public static void Warning (String msg) { /* pdt */
		if (errDist >= minErrDist) Errors.Warn(token.line, token.col, msg);
		errDist = 0;
	}

	public static boolean Successful() { /* pdt */
		return Errors.count == 0;
	}

	public static String LexString() { /* pdt */
		return token.val;
	}

	public static String LookAheadString() { /* pdt */
		return la.val;
	}

	static void Get () {
		for (;;) {
			token = la; /* pdt */
			la = Scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = token; /* pdt */
		}
	}

	static void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}

	static boolean StartOf (int s) {
		return set[s][la.kind];
	}

	static void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}

	static boolean WeakSeparator (int n, int syFol, int repFol) {
		boolean[] s = new boolean[maxT+1];
		if (la.kind == n) { Get(); return true; }
		else if (StartOf(repFol)) return false;
		else {
			for (int i=0; i <= maxT; i++) {
				s[i] = set[syFol][i] || set[repFol][i] || set[0][i];
			}
			SynErr(n);
			while (!s[la.kind]) Get();
			return StartOf(syFol);
		}
	}

	static void compilationunit() {
		while (la.kind == 6) {
			Clases();
		}
	}

	static void Clases() {
		Expect(6);
		Expect(1);
		Expect(17);
		Miembros();
		Expect(23);
	}

	static void TiposSimples() {
		if (la.kind == 8) {
			Get();
		} else if (la.kind == 4) {
			Get();
		} else if (la.kind == 13) {
			Get();
		} else SynErr(42);
	}

	static void Declaraciones() {
		if (la.kind == 34 || la.kind == 35) {
			if (la.kind == 34) {
				Get();
			} else {
				Get();
			}
		}
		TiposSimples();
		if (la.kind == 1) {
			Get();
		} else if (la.kind == 36) {
			Get();
		} else SynErr(43);
		if (la.kind == 18 || la.kind == 19 || la.kind == 31) {
			if (la.kind == 31) {
				Get();
			} else if (la.kind == 18) {
				Get();
				Expresion();
				Expect(24);
				if (la.kind == 31) {
					Get();
				}
			} else {
				Get();
				ArgumentosDeclaracion();
				Expect(25);
				Expect(17);
				Miembros();
				Expect(23);
			}
		}
	}

	static void Expresion() {
		if (StartOf(1)) {
			if (la.kind == 33) {
				Get();
				Expresion();
				Expect(14);
				Expresion();
				Expresion1();
			}
		} else if (StartOf(2)) {
			Expresion1();
		} else SynErr(44);
	}

	static void ArgumentosDeclaracion() {
		if (StartOf(3)) {
			Declaraciones();
			while (la.kind == 15) {
				Get();
				ArgumentosDeclaracion();
			}
		}
	}

	static void Miembros() {
		while (StartOf(4)) {
			if (StartOf(3)) {
				Declaraciones();
			} else {
				Sentencias();
			}
		}
	}

	static void Asignaciones() {
		Expect(1);
		if (StartOf(5)) {
			if (la.kind == 16) {
				Get();
				Expect(1);
				if (la.kind == 19) {
					Get();
					Argumentos();
					Expect(25);
					Expect(31);
				}
			} else if (la.kind == 1) {
				Get();
				Expect(31);
			} else if (la.kind == 18) {
				Get();
				Expresion();
				Expect(24);
			} else {
				Get();
				if (StartOf(6)) {
					if (StartOf(7)) {
						ArgumentosDeclaracion();
					} else {
						Argumentos();
					}
				}
				Expect(25);
				if (la.kind == 17 || la.kind == 31) {
					if (la.kind == 17) {
						Get();
						Miembros();
						Expect(23);
					} else {
						Get();
					}
				}
			}
		}
		ALLMetodo();
	}

	static void Argumentos() {
		if (StartOf(8)) {
			Expresion();
			while (la.kind == 15) {
				Get();
				Argumentos();
			}
		}
	}

	static void ALLMetodo() {
		if (la.kind == 30) {
			Get();
			Expresion();
			Expect(31);
		}
	}

	static void ES() {
		if (la.kind == 37) {
			Escribir();
		} else if (la.kind == 38) {
			Leer();
		} else SynErr(45);
	}

	static void Escribir() {
		Expect(37);
		Expect(19);
		if (StartOf(9)) {
			if (la.kind == 3) {
				Get();
			} else {
				Expresion();
			}
		}
		Expect(25);
		Expect(31);
	}

	static void Leer() {
		Expect(38);
		Expect(19);
		Expresion();
		Expect(25);
		Expect(31);
	}

	static void Retorno() {
		Expect(39);
		if (StartOf(10)) {
			Expresion();
		}
		Expect(31);
	}

	static void For() {
		Expect(40);
		Expect(19);
		Asignaciones();
		Expresion();
		Expect(31);
		Asignaciones();
		Expect(25);
		if (la.kind == 17) {
			Get();
			while (StartOf(11)) {
				Sentencias();
			}
			Expect(23);
		} else if (StartOf(11)) {
			Sentencias();
		} else SynErr(46);
	}

	static void Sentencias() {
		if (la.kind == 1) {
			Asignaciones();
		} else if (la.kind == 39) {
			Retorno();
		} else if (la.kind == 37 || la.kind == 38) {
			ES();
		} else if (la.kind == 40) {
			For();
		} else SynErr(47);
	}

	static void Expresion1() {
		if (StartOf(2)) {
			Expresion1();
			Expect(28);
			Expresion2();
		} else if (StartOf(2)) {
			Expresion2();
		} else SynErr(48);
	}

	static void Expresion2() {
		if (StartOf(2)) {
			Expresion2();
			Expect(22);
			Expresion3();
		} else if (StartOf(2)) {
			Expresion3();
		} else SynErr(49);
	}

	static void Expresion3() {
		if (la.kind == 21) {
			Get();
			Expresion3();
		} else if (StartOf(12)) {
			Expresion4();
		} else SynErr(50);
	}

	static void Expresion4() {
		switch (la.kind) {
		case 19: {
			Get();
			Expresion();
			Expect(25);
			break;
		}
		case 9: {
			Get();
			Expect(1);
			Expect(19);
			Argumentos();
			Expect(25);
			break;
		}
		case 1: {
			Get();
			if (la.kind == 16 || la.kind == 18 || la.kind == 19) {
				if (la.kind == 16) {
					Get();
					Expect(1);
					if (la.kind == 19) {
						Get();
						Argumentos();
						Expect(25);
					}
				} else if (la.kind == 19) {
					Get();
					Argumentos();
					Expect(25);
				} else {
					Get();
					Expresion();
					Expect(24);
				}
			}
			break;
		}
		case 2: {
			Get();
			break;
		}
		case 3: {
			Get();
			break;
		}
		case 12: {
			Get();
			break;
		}
		case 7: {
			Get();
			break;
		}
		default: SynErr(51); break;
		}
	}



	public static void Parse() {
		la = new Token();
		la.val = "";
		Get();
		compilationunit();
		Expect(0);

	}

	private static boolean[][] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,T,T, x,x,x,T, x,T,x,x, T,T,x,x, x,x,x,T, x,T,x,x, x,x,x,x, x,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,x,x, x,x,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
		{x,x,x,x, T,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x},
		{x,T,x,x, T,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,T,T,T, T,x,x},
		{x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
		{x,T,T,T, T,x,x,T, T,T,x,x, T,T,x,T, x,x,x,T, x,T,x,x, x,T,x,x, x,x,x,x, x,T,T,T, x,x,x,x, x,x,x},
		{x,x,x,x, T,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,x,T, x,x,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,x,x, x,x,x,T, x,T,x,x, x,T,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,x,x, x,x,x,T, x,T,x,x, x,x,x,x, x,x,x,T, x,T,x,x, x,x,x,x, x,x,x},
		{x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,x,x},
		{x,T,T,T, x,x,x,T, x,T,x,x, T,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}

	};

} // end Parser

/* pdt - considerable extension from here on */

class ErrorRec {
	public int line, col, num;
	public String str;
	public ErrorRec next;

	public ErrorRec(int l, int c, String s) {
		line = l; col = c; str = s; next = null;
	}

} // end ErrorRec

class Errors {

	public static int count = 0;                                     // number of errors detected
	public static String errMsgFormat = "file {0} : ({1}, {2}) {3}"; // 0=file 1=line, 2=column, 3=text
	static String fileName = "";
	static String listName = "";
	static boolean mergeErrors = false;
	static PrintWriter mergedList;

	static ErrorRec first = null, last;
	static boolean eof = false;

	static String getLine() {
		char ch, CR = '\r', LF = '\n';
		int l = 0;
		StringBuffer s = new StringBuffer();
		ch = (char) Buffer.Read();
		while (ch != Buffer.EOF && ch != CR && ch != LF) {
			s.append(ch); l++; ch = (char) Buffer.Read();
		}
		eof = (l == 0 && ch == Buffer.EOF);
		if (ch == CR) {  // check for MS-DOS
			ch = (char) Buffer.Read();
			if (ch != LF && ch != Buffer.EOF) Buffer.pos--;
		}
		return s.toString();
	}

	static private String Int(int n, int len) {
		String s = String.valueOf(n);
		int i = s.length(); if (len < i) len = i;
		int j = 0, d = len - s.length();
		char[] a = new char[len];
		for (i = 0; i < d; i++) a[i] = ' ';
		for (j = 0; i < len; i++) {a[i] = s.charAt(j); j++;}
		return new String(a, 0, len);
	}

	static void display(String s, ErrorRec e) {
		mergedList.print("**** ");
		for (int c = 1; c < e.col; c++)
			if (s.charAt(c-1) == '\t') mergedList.print("\t"); else mergedList.print(" ");
		mergedList.println("^ " + e.str);
	}

	public static void Init (String fn, String dir, boolean merge) {
		fileName = fn;
		listName = dir + "listing.txt";
		mergeErrors = merge;
		if (mergeErrors)
			try {
				mergedList = new PrintWriter(new BufferedWriter(new FileWriter(listName, false)));
			} catch (IOException e) {
				Errors.Exception("-- could not open " + listName);
			}
	}

	public static void Summarize () {
		if (mergeErrors) {
			ErrorRec cur = first;
			Buffer.setPos(0);
			int lnr = 1;
			String s = getLine();
			while (!eof) {
				mergedList.println(Int(lnr, 4) + " " + s);
				while (cur != null && cur.line == lnr) {
					display(s, cur); cur = cur.next;
				}
				lnr++; s = getLine();
			}
			if (cur != null) {
				mergedList.println(Int(lnr, 4));
				while (cur != null) {
					display(s, cur); cur = cur.next;
				}
			}
			mergedList.println();
			mergedList.println(count + " errors detected");
			mergedList.close();
		}
		switch (count) {
			case 0 : System.out.println("Parsed correctly"); break;
			case 1 : System.out.println("1 error detected"); break;
			default: System.out.println(count + " errors detected"); break;
		}
		if (count > 0 && mergeErrors) System.out.println("see " + listName);
	}

	public static void storeError (int line, int col, String s) {
		if (mergeErrors) {
			ErrorRec latest = new ErrorRec(line, col, s);
			if (first == null) first = latest; else last.next = latest;
			last = latest;
		} else printMsg(fileName, line, col, s);
	}

	public static void SynErr (int line, int col, int n) {
		String s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "ident expected"; break;
			case 2: s = "enteros expected"; break;
			case 3: s = "cadenaCar expected"; break;
			case 4: s = "boolean expected"; break;
			case 5: s = "char expected"; break;
			case 6: s = "class expected"; break;
			case 7: s = "false expected"; break;
			case 8: s = "int expected"; break;
			case 9: s = "new expected"; break;
			case 10: s = "short expected"; break;
			case 11: s = "static expected"; break;
			case 12: s = "true expected"; break;
			case 13: s = "void expected"; break;
			case 14: s = "dosPuntos expected"; break;
			case 15: s = "comma expected"; break;
			case 16: s = "punto expected"; break;
			case 17: s = "llaveIzda expected"; break;
			case 18: s = "corcheteIzda expected"; break;
			case 19: s = "parentesisIzda expected"; break;
			case 20: s = "menos expected"; break;
			case 21: s = "not expected"; break;
			case 22: s = "mas expected"; break;
			case 23: s = "llaveDer expected"; break;
			case 24: s = "corcheteDer expected"; break;
			case 25: s = "parentesisDer expected"; break;
			case 26: s = "multiplicacion expected"; break;
			case 27: s = "div expected"; break;
			case 28: s = "menor expected"; break;
			case 29: s = "mayor expected"; break;
			case 30: s = "igual expected"; break;
			case 31: s = "puntoComa expected"; break;
			case 32: s = "doblesComillas expected"; break;
			case 33: s = "interrogacion expected"; break;
			case 34: s = "\"public\" expected"; break;
			case 35: s = "\"private\" expected"; break;
			case 36: s = "\"main\" expected"; break;
			case 37: s = "\"print\" expected"; break;
			case 38: s = "\"read\" expected"; break;
			case 39: s = "\"return\" expected"; break;
			case 40: s = "\"for\" expected"; break;
			case 41: s = "??? expected"; break;
			case 42: s = "invalid TiposSimples"; break;
			case 43: s = "invalid Declaraciones"; break;
			case 44: s = "invalid Expresion"; break;
			case 45: s = "invalid ES"; break;
			case 46: s = "invalid For"; break;
			case 47: s = "invalid Sentencias"; break;
			case 48: s = "invalid Expresion1"; break;
			case 49: s = "invalid Expresion2"; break;
			case 50: s = "invalid Expresion3"; break;
			case 51: s = "invalid Expresion4"; break;
			default: s = "error " + n; break;
		}
		storeError(line, col, s);
		count++;
	}

	public static void SemErr (int line, int col, int n) {
		storeError(line, col, ("error " + n));
		count++;
	}

	public static void Error (int line, int col, String s) {
		storeError(line, col, s);
		count++;
	}

	public static void Warn (int line, int col, String s) {
		storeError(line, col, s);
	}

	public static void Exception (String s) {
		System.out.println(s);
		System.exit(1);
	}

	private static void printMsg(String fileName, int line, int column, String msg) {
		StringBuffer b = new StringBuffer(errMsgFormat);
		int pos = b.indexOf("{0}");
		if (pos >= 0) { b.replace(pos, pos+3, fileName); }
		pos = b.indexOf("{1}");
		if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, line); }
		pos = b.indexOf("{2}");
		if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, column); }
		pos = b.indexOf("{3}");
		if (pos >= 0) b.replace(pos, pos+3, msg);
		System.out.println(b.toString());
	}

} // end Errors
